#include <sdk.h>
#include "classbrowserbuilderthread.h"

#define CBBT_SANITY_CHECK ((!::wxIsMainThread() && m_TerminationRequested) || Manager::IsAppShuttingDown())
#define CC_BUILDERTHREAD_DEBUG_OUTPUT 0

ClassBrowserBuilderThread::ClassBrowserBuilderThread( wxEvtHandler* evtHandler, wxSemaphore& sem ) :
  wxThread( wxTHREAD_JOINABLE ),
  m_Parent( evtHandler ),
  m_ClassBrowserSemaphore( sem ),
  m_ClassBrowserBuilderThreadMutex(),
  m_NativeParser( 0 ),
  m_CCTreeCtrlTop( 0 ),
  m_CCTreeCtrlBottom( 0 ),
  m_UserData( 0 ),
  m_BrowserOptions(),
  m_TokenTree( 0 ),
  m_InitDone( false ),
  m_TerminationRequested( false ),
  m_idThreadEvent( wxID_NONE ) {
}

ClassBrowserBuilderThread::~ClassBrowserBuilderThread() {
}

void ClassBrowserBuilderThread::Init( NativeParser* np, CCTreeCtrl* treeTop, CCTreeCtrl* treeBottom,
                                      const wxString& active_filename, void* user_data, const BrowserOptions& bo,
                                      TokenTree* tt, int idThreadEvent ) {
  CC_LOCKER_TRACK_CBBT_MTX_LOCK( m_ClassBrowserBuilderThreadMutex );
  m_NativeParser = np;
  m_CCTreeCtrlTop = treeTop;
  m_CCTreeCtrlBottom = treeBottom;
  m_ActiveFilename = active_filename;
  m_UserData = user_data;
  m_BrowserOptions = bo;
  m_TokenTree = tt;
  m_idThreadEvent = idThreadEvent;
  m_CurrentFileSet.clear();
  m_CurrentTokenSet.clear();
  TokenTree* tree = m_NativeParser->GetParser().GetTokenTree();
  if( m_BrowserOptions.displayFilter == bdfFile && !m_ActiveFilename.IsEmpty() ) {
    wxArrayString paths = m_NativeParser->GetAllPathsByFilename( m_ActiveFilename );
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    TokenFileSet result;
    for( size_t i = 0; i < paths.GetCount(); ++i ) {
      tree->GetFileMatches( paths[i], result, true, true );
      for( TokenFileSet::const_iterator tfs_it = result.begin(); tfs_it != result.end(); ++tfs_it ) {
        m_CurrentFileSet.insert( *tfs_it );
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  } else if( m_BrowserOptions.displayFilter == bdfProject && m_UserData ) {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    M_Code_Project* prj = static_cast<M_Code_Project*>( m_UserData );
    for( FilesList::const_iterator fl_it = prj->GetFilesList().begin(); fl_it != prj->GetFilesList().end(); ++fl_it ) {
      ProjectFile* curFile = *fl_it;
      if( !curFile ) {
        continue;
      }
      const size_t fileIdx = tree->GetFileIndex( curFile->file.GetFullPath() );
      if( fileIdx ) {
        m_CurrentFileSet.insert( fileIdx );
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  }
  if( !m_CurrentFileSet.empty() ) {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    m_CurrentTokenSet.clear();
    m_CurrentGlobalTokensSet.clear();
    for( TokenFileSet::const_iterator itf = m_CurrentFileSet.begin(); itf != m_CurrentFileSet.end(); ++itf ) {
      const TokenIdxSet* tokens = tree->GetTokensBelongToFile( *itf );
      if( !tokens ) {
        continue;
      }
      for( TokenIdxSet::const_iterator its = tokens->begin(); its != tokens->end(); ++its ) {
        Token* curToken = tree->at( *its );
        if( curToken ) {
          m_CurrentTokenSet.insert( *its );
          if( curToken->m_ParentIndex == -1 ) {
            m_CurrentGlobalTokensSet.insert( *its );
          }
        }
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  }
  CC_LOCKER_TRACK_CBBT_MTX_UNLOCK( m_ClassBrowserBuilderThreadMutex )
}


void* ClassBrowserBuilderThread::Entry() {
  while( !m_TerminationRequested && !Manager::IsAppShuttingDown() ) {
    m_ClassBrowserSemaphore.Wait();
    if( m_TerminationRequested || Manager::IsAppShuttingDown() ) {
      break;
    }
    if( !::wxIsMainThread() ) {
      ::wxMutexGuiEnter();
    }
    BuildTree();
    if( !::wxIsMainThread() ) {
      ::wxMutexGuiLeave();
    }
  }
  m_NativeParser = 0;
  m_CCTreeCtrlTop = 0;
  m_CCTreeCtrlBottom = 0;
  return 0;
}


void ClassBrowserBuilderThread::ExpandItem( wxTreeItemId item ) {
  if( CBBT_SANITY_CHECK || !item.IsOk() ) {
    return;
  }
  bool locked = false;
  if( m_InitDone ) {
    CC_LOCKER_TRACK_CBBT_MTX_LOCK( m_ClassBrowserBuilderThreadMutex )
    locked = true;
  }
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( m_CCTreeCtrlTop->GetItemData( item ) );
  if( data ) {
    m_TokenTree->RecalcInheritanceChain( data->m_Token );
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( data ) {
    switch( data->m_SpecialFolder ) {
      case sfRoot: {
        CreateSpecialFolders( m_CCTreeCtrlTop, item );
        if( !( m_BrowserOptions.displayFilter == bdfFile
               && m_ActiveFilename.IsEmpty() ) ) {
          AddChildrenOf( m_CCTreeCtrlTop, item, -1, ~( tkFunction | tkVariable | tkMacroDef | tkTypedef | tkMacroUse ) );
        }
        break;
      }
      case sfBase:
        AddAncestorsOf( m_CCTreeCtrlTop, item, data->m_Token->m_Index );
        break;
      case sfDerived:
        AddDescendantsOf( m_CCTreeCtrlTop, item, data->m_Token->m_Index, false );
        break;
      case sfToken: {
        short int kind = 0;
        switch( data->m_Token->m_TokenKind ) {
          case tkClass: {
            if( m_BrowserOptions.showInheritance ) {
              wxTreeItemId base = m_CCTreeCtrlTop->AppendItem( item, _T( "Base classes" ),
                                  PARSER_IMG_CLASS_FOLDER, PARSER_IMG_CLASS_FOLDER,
                                  new CCTreeCtrlData( sfBase, data->m_Token, tkClass, data->m_Token->m_Index ) );
              if( !data->m_Token->m_DirectAncestors.empty() )
              { m_CCTreeCtrlTop->SetItemHasChildren( base ); }
              wxTreeItemId derived = m_CCTreeCtrlTop->AppendItem( item, _T( "Derived classes" ),
                                     PARSER_IMG_CLASS_FOLDER, PARSER_IMG_CLASS_FOLDER,
                                     new CCTreeCtrlData( sfDerived, data->m_Token, tkClass, data->m_Token->m_Index ) );
              if( !data->m_Token->m_Descendants.empty() )
              { m_CCTreeCtrlTop->SetItemHasChildren( derived ); }
            }
            kind = tkClass | tkEnum;
            break;
          }
          case tkNamespace:
            kind = tkNamespace | tkClass | tkEnum;
            break;
          case tkEnum:
          case tkTypedef:
          case tkConstructor:
          case tkDestructor:
          case tkFunction:
          case tkVariable:
          case tkEnumerator:
          case tkMacroDef:
          case tkMacroUse:
          case tkAnyContainer:
          case tkAnyFunction:
          case tkUndefined:
          default:
            break;
        }
        if( kind != 0 ) {
          AddChildrenOf( m_CCTreeCtrlTop, item, data->m_Token->m_Index, kind );
        }
        break;
      }
      case sfGFuncs:
      case sfGVars:
      case sfPreproc:
      case sfTypedef:
      case sfMacro:
      default:
        break;
    }
  }
  if( m_NativeParser && !m_BrowserOptions.treeMembers ) {
    AddMembersOf( m_CCTreeCtrlTop, item );
  }
  if( locked )
    CC_LOCKER_TRACK_CBBT_MTX_UNLOCK( m_ClassBrowserBuilderThreadMutex )
  }

void ClassBrowserBuilderThread::CollapseItem( wxTreeItemId item ) {
  if( CBBT_SANITY_CHECK || !item.IsOk() ) {
    return;
  }
  bool locked = false;
  if( m_InitDone ) {
    CC_LOCKER_TRACK_CBBT_MTX_LOCK( m_ClassBrowserBuilderThreadMutex )
    locked = true;
  }
  m_CCTreeCtrlTop->DeleteChildren( item );
  m_CCTreeCtrlTop->SetItemHasChildren( item );
  if( locked )
    CC_LOCKER_TRACK_CBBT_MTX_UNLOCK( m_ClassBrowserBuilderThreadMutex )
  }

void ClassBrowserBuilderThread::SelectItem( wxTreeItemId item ) {
  if( CBBT_SANITY_CHECK || !item.IsOk() ) {
    return;
  }
  CC_LOCKER_TRACK_CBBT_MTX_LOCK( m_ClassBrowserBuilderThreadMutex )
  CCTreeCtrl* tree = ( m_BrowserOptions.treeMembers ) ? m_CCTreeCtrlBottom : m_CCTreeCtrlTop;
  if( !( m_BrowserOptions.displayFilter == bdfFile && m_ActiveFilename.IsEmpty() ) ) {
    AddMembersOf( tree, item );
  }
  CC_LOCKER_TRACK_CBBT_MTX_UNLOCK( m_ClassBrowserBuilderThreadMutex )
}

void ClassBrowserBuilderThread::SelectItemRequired() {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  if( m_SelectItemRequired.IsOk() ) {
    m_CCTreeCtrlTop->SelectItem( m_SelectItemRequired );
    m_CCTreeCtrlTop->EnsureVisible( m_SelectItemRequired );
  }
}

void ClassBrowserBuilderThread::BuildTree() {
  if( CBBT_SANITY_CHECK || !m_CCTreeCtrlTop || !m_CCTreeCtrlBottom || !m_NativeParser ) {
    return;
  }
  wxCommandEvent e1( wxEVT_COMMAND_ENTER, m_idThreadEvent );
  e1.SetInt( buildTreeStart );
  m_Parent->AddPendingEvent( e1 );
  m_CCTreeCtrlTop->SetImageList( m_NativeParser->GetImageList() );
  m_CCTreeCtrlBottom->SetImageList( m_NativeParser->GetImageList() );
  wxTreeItemId root = m_CCTreeCtrlTop->GetRootItem();
  if( !root.IsOk() ) {
    root = m_CCTreeCtrlTop->AddRoot( _T( "符号浏览" ), PARSER_IMG_SYMBOLS_FOLDER, PARSER_IMG_SYMBOLS_FOLDER, new CCTreeCtrlData( sfRoot ) );
    m_CCTreeCtrlTop->SetItemHasChildren( root );
  }
  m_CCTreeCtrlTop->SetCompareFunction( m_BrowserOptions.sortType );
  m_CCTreeCtrlBottom->SetCompareFunction( m_BrowserOptions.sortType );
  m_ExpandedVect.clear();
  SaveExpandedItems( m_CCTreeCtrlTop, root, 0 );
  SaveSelectedItem();
  if( m_BrowserOptions.treeMembers && m_CCTreeCtrlBottom ) {
    m_CCTreeCtrlBottom->Hide();
    m_CCTreeCtrlBottom->Freeze();
  }
  m_CCTreeCtrlTop->Hide();
  m_CCTreeCtrlTop->Freeze();
  RemoveInvalidNodes( m_CCTreeCtrlTop, root );
  if( m_BrowserOptions.treeMembers ) {
    RemoveInvalidNodes( m_CCTreeCtrlBottom, m_CCTreeCtrlBottom->GetRootItem() );
  }
  if( CBBT_SANITY_CHECK ) {
    return;
  }
  CollapseItem( root );
  m_CCTreeCtrlTop->Expand( root );
  ExpandItem( root );
  ExpandSavedItems( m_CCTreeCtrlTop, root, 0 );
  SelectSavedItem();
  if( m_BrowserOptions.treeMembers ) {
    m_CCTreeCtrlBottom->Thaw();
    m_CCTreeCtrlBottom->Show();
  }
  ExpandNamespaces( m_CCTreeCtrlTop->GetRootItem(), tkNamespace, 1 );
  ExpandNamespaces( m_CCTreeCtrlTop->GetRootItem(), tkClass, 1 );
  m_CCTreeCtrlTop->Thaw();
  m_CCTreeCtrlTop->Show();
  m_InitDone = true;
  wxCommandEvent e2( wxEVT_COMMAND_ENTER, m_idThreadEvent );
  e2.SetInt( buildTreeEnd );
  m_Parent->AddPendingEvent( e2 );
}

void ClassBrowserBuilderThread::RemoveInvalidNodes( CCTreeCtrl* tree, wxTreeItemId parent ) {
  if( CBBT_SANITY_CHECK || !parent.IsOk() ) {
    return;
  }
  wxTreeItemId existing = tree->GetLastChild( parent );
  while( parent.IsOk() && existing.IsOk() ) {
    bool removeCurrent = false;
    bool hasChildren = tree->ItemHasChildren( existing );
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( tree->GetItemData( existing ) );
    if( tree == m_CCTreeCtrlBottom ) {
      removeCurrent = true;
    } else if( data && data->m_Token ) {
      const Token* token = nullptr;
      {
        CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
        token = m_TokenTree->at( data->m_TokenIndex );
        CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      }
      if( token != data->m_Token || ( data->m_Ticket && data->m_Ticket != data->m_Token->GetTicket() )
          || !TokenMatchesFilter( data->m_Token ) ) {
        removeCurrent = true;
      }
    }
    if( removeCurrent ) {
      if( hasChildren ) {
        tree->DeleteChildren( existing );
      }
      wxTreeItemId next = tree->GetPrevSibling( existing );
      if( !next.IsOk() && parent.IsOk() && tree == m_CCTreeCtrlTop && tree->GetChildrenCount( parent, false ) == 1 ) {
        CollapseItem( parent );
        return;
      } else {
        tree->Delete( existing );
        existing = next;
        continue;
      }
    } else
    { RemoveInvalidNodes( tree, existing ); }
    if( existing.IsOk() ) {
      existing = tree->GetPrevSibling( existing );
    }
  }
}

void ClassBrowserBuilderThread::ExpandNamespaces( wxTreeItemId node, TokenKind tokenKind, int level ) {
  if( CBBT_SANITY_CHECK || !m_BrowserOptions.expandNS || !node.IsOk() || level <= 0 ) {
    return;
  }
  wxTreeItemIdValue enumerationCookie;
  wxTreeItemId existing = m_CCTreeCtrlTop->GetFirstChild( node, enumerationCookie );
  while( existing.IsOk() ) {
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( m_CCTreeCtrlTop->GetItemData( existing ) );
    if( data && data->m_Token && ( data->m_Token->m_TokenKind == tokenKind ) ) {
      m_CCTreeCtrlTop->Expand( existing );
      ExpandNamespaces( existing, tokenKind, level - 1 );
    }
    existing = m_CCTreeCtrlTop->GetNextSibling( existing );
  }
}

bool ClassBrowserBuilderThread::CreateSpecialFolders( CCTreeCtrl* tree, wxTreeItemId parent ) {
  bool hasGF = false;
  bool hasGV = false;
  bool hasGP = false;
  bool hasTD = false;
  bool hasGM = false;
  TokenTree* tt = m_NativeParser->GetParser().GetTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  const TokenIdxSet* tis = tt->GetGlobalNameSpaces();
  for( TokenIdxSet::const_iterator tis_it = tis->begin(); tis_it != tis->end(); ++tis_it ) {
    const Token* token = tt->at( *tis_it );
    if( token && token->m_IsLocal && TokenMatchesFilter( token, true ) ) {
      if( !hasGF && token->m_TokenKind == tkFunction ) {
        hasGF = true;
      } else if( !hasGM && token->m_TokenKind == tkMacroUse ) {
        hasGM = true;
      } else if( !hasGV && token->m_TokenKind == tkVariable ) {
        hasGV = true;
      } else if( !hasGP && token->m_TokenKind == tkMacroDef ) {
        hasGP = true;
      } else if( !hasTD && token->m_TokenKind == tkTypedef ) {
        hasTD = true;
      }
    }
    if( hasGF && hasGV && hasGP && hasTD && hasGM ) {
      break;
    }
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  wxTreeItemId gfuncs = AddNodeIfNotThere( m_CCTreeCtrlTop, parent, _T( "全局函数" ),
                        PARSER_IMG_FUNCS_FOLDER, new CCTreeCtrlData( sfGFuncs, 0, tkFunction, -1 ) );
  wxTreeItemId tdef = AddNodeIfNotThere( m_CCTreeCtrlTop, parent, _T( "全局类型" ),
                                         PARSER_IMG_TYPEDEF_FOLDER, new CCTreeCtrlData( sfTypedef, 0, tkTypedef, -1 ) );
  wxTreeItemId gvars = AddNodeIfNotThere( m_CCTreeCtrlTop, parent, _T( "全局变量" ),
                                          PARSER_IMG_VARS_FOLDER, new CCTreeCtrlData( sfGVars, 0, tkVariable, -1 ) );
  wxTreeItemId preproc = AddNodeIfNotThere( m_CCTreeCtrlTop, parent, _T( "宏定义" ),
                         PARSER_IMG_MACRO_DEF_FOLDER, new CCTreeCtrlData( sfPreproc, 0, tkMacroDef, -1 ) );
  wxTreeItemId gmacro = AddNodeIfNotThere( m_CCTreeCtrlTop, parent, _T( "宏用法" ),
                        PARSER_IMG_MACRO_USE_FOLDER, new CCTreeCtrlData( sfMacro, 0, tkMacroUse, -1 ) );
  bool bottom = m_BrowserOptions.treeMembers;
  m_CCTreeCtrlTop->SetItemHasChildren( gfuncs, !bottom && hasGF );
  m_CCTreeCtrlTop->SetItemHasChildren( tdef, !bottom && hasTD );
  m_CCTreeCtrlTop->SetItemHasChildren( gvars, !bottom && hasGV );
  m_CCTreeCtrlTop->SetItemHasChildren( preproc, !bottom && hasGP );
  m_CCTreeCtrlTop->SetItemHasChildren( gmacro, !bottom && hasGM );
  wxColour black = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
  wxColour grey = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
  tree->SetItemTextColour( gfuncs, hasGF ? black : grey );
  tree->SetItemTextColour( gvars, hasGV ? black : grey );
  tree->SetItemTextColour( preproc, hasGP ? black : grey );
  tree->SetItemTextColour( tdef, hasTD ? black : grey );
  tree->SetItemTextColour( gmacro, hasGM ? black : grey );
  return hasGF || hasGV || hasGP || hasTD || hasGM;
}

wxTreeItemId ClassBrowserBuilderThread::AddNodeIfNotThere( CCTreeCtrl* tree, wxTreeItemId parent, const wxString& name, int imgIndex, CCTreeCtrlData* data ) {
  wxTreeItemIdValue cookie = 0;
  wxTreeItemId existing = tree->GetFirstChild( parent, cookie );
  while( existing ) {
    wxString itemText = tree->GetItemText( existing );
    if( itemText == name ) {
      tree->SetItemImage( existing, imgIndex, wxTreeItemIcon_Normal );
      tree->SetItemImage( existing, imgIndex, wxTreeItemIcon_Selected );
      delete tree->GetItemData( existing );
      tree->SetItemData( existing, data );
      return existing;
    }
    existing = tree->GetNextChild( parent, cookie );
  }
  return tree->AppendItem( parent, name, imgIndex, imgIndex, data );
}

bool ClassBrowserBuilderThread::AddChildrenOf( CCTreeCtrl* tree, wxTreeItemId parent, int parentTokenIdx, short int tokenKindMask, int tokenScopeMask ) {
  if( CBBT_SANITY_CHECK ) {
    return false;
  }
  const Token* parentToken = 0;
  bool parentTokenError = false;
  const TokenIdxSet* tokens = 0;
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  if( parentTokenIdx == -1 ) {
    if( m_BrowserOptions.displayFilter == bdfWorkspace
        || m_BrowserOptions.displayFilter == bdfEverything ) {
      tokens = m_TokenTree->GetGlobalNameSpaces();
    } else
    { tokens = &m_CurrentGlobalTokensSet; }
  } else {
    parentToken = m_TokenTree->at( parentTokenIdx );
    if( !parentToken ) {
      parentTokenError = true;
    }
    if( !parentTokenError ) {
      tokens = &parentToken->m_Children;
    }
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( parentTokenError ) {
    return false;
  }
  return AddNodes( tree, parent, tokens, tokenKindMask, tokenScopeMask,
                   m_BrowserOptions.displayFilter == bdfEverything );
}

bool ClassBrowserBuilderThread::AddAncestorsOf( CCTreeCtrl* tree, wxTreeItemId parent, int tokenIdx ) {
  if( CBBT_SANITY_CHECK ) {
    return false;
  }
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  Token* token = m_TokenTree->at( tokenIdx );
  if( token ) {
    m_TokenTree->RecalcInheritanceChain( token );
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( !token ) {
    return false;
  }
  return AddNodes( tree, parent, &token->m_DirectAncestors, tkClass | tkTypedef, 0, true );
}

bool ClassBrowserBuilderThread::AddDescendantsOf( CCTreeCtrl* tree, wxTreeItemId parent, int tokenIdx, bool allowInheritance ) {
  if( CBBT_SANITY_CHECK ) {
    return false;
  }
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  Token* token = m_TokenTree->at( tokenIdx );
  if( token ) {
    m_TokenTree->RecalcInheritanceChain( token );
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( !token ) {
    return false;
  }
  bool oldShowInheritance = m_BrowserOptions.showInheritance;
  m_BrowserOptions.showInheritance = allowInheritance;
  bool ret = AddNodes( tree, parent, &token->m_Descendants, tkClass | tkTypedef, 0, true );
  m_BrowserOptions.showInheritance = oldShowInheritance;
  return ret;
}

void ClassBrowserBuilderThread::AddMembersOf( CCTreeCtrl* tree, wxTreeItemId node ) {
  if( CBBT_SANITY_CHECK || !node.IsOk() ) {
    return;
  }
  CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( m_CCTreeCtrlTop->GetItemData( node ) );
  bool bottom = ( tree == m_CCTreeCtrlBottom );
  if( bottom ) {
    tree->Freeze();
    tree->DeleteAllItems();
    node = tree->AddRoot( _T( "Members" ) );
  }
  wxTreeItemId firstItem;
  bool haveFirstItem = false;
  if( data ) {
    switch( data->m_SpecialFolder ) {
      case sfGFuncs :
        AddChildrenOf( tree, node, -1, tkFunction, false );
        break;
      case sfGVars :
        AddChildrenOf( tree, node, -1, tkVariable, false );
        break;
      case sfPreproc :
        AddChildrenOf( tree, node, -1, tkMacroDef, false );
        break;
      case sfTypedef :
        AddChildrenOf( tree, node, -1, tkTypedef, false );
        break;
      case sfMacro :
        AddChildrenOf( tree, node, -1, tkMacroUse, false );
        break;
      case sfToken: {
        if( bottom ) {
          if( m_BrowserOptions.sortType == bstKind && !( data->m_Token->m_TokenKind & tkEnum ) ) {
            wxTreeItemId rootCtorDtor = tree->AppendItem( node, _T( "构造函数/析构函数" ), PARSER_IMG_CLASS_FOLDER );
            wxTreeItemId rootFuncs = tree->AppendItem( node, _T( "函数" ), PARSER_IMG_FUNCS_FOLDER );
            wxTreeItemId rootVars = tree->AppendItem( node, _T( "变量" ), PARSER_IMG_VARS_FOLDER );
            wxTreeItemId rootMacro = tree->AppendItem( node, _T( "宏" ), PARSER_IMG_MACRO_USE_FOLDER );
            wxTreeItemId rootOthers = tree->AppendItem( node, _T( "其他" ), PARSER_IMG_OTHERS_FOLDER );
            AddChildrenOf( tree, rootCtorDtor, data->m_Token->m_Index, tkConstructor | tkDestructor );
            AddChildrenOf( tree, rootFuncs, data->m_Token->m_Index, tkFunction );
            AddChildrenOf( tree, rootVars, data->m_Token->m_Index, tkVariable );
            AddChildrenOf( tree, rootMacro, data->m_Token->m_Index, tkMacroUse );
            AddChildrenOf( tree, rootOthers, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum | tkAnyFunction | tkVariable | tkMacroUse ) );
            firstItem = rootCtorDtor;
          } else if( m_BrowserOptions.sortType == bstScope && data->m_Token->m_TokenKind & tkClass ) {
            wxTreeItemId rootPublic = tree->AppendItem( node, _T( "公共" ), PARSER_IMG_CLASS_FOLDER );
            wxTreeItemId rootProtected = tree->AppendItem( node, _T( "保护" ), PARSER_IMG_FUNCS_FOLDER );
            wxTreeItemId rootPrivate = tree->AppendItem( node, _T( "私有" ), PARSER_IMG_VARS_FOLDER );
            AddChildrenOf( tree, rootPublic, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum ), tsPublic );
            AddChildrenOf( tree, rootProtected, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum ), tsProtected );
            AddChildrenOf( tree, rootPrivate, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum ), tsPrivate );
            firstItem = rootPublic;
          } else {
            AddChildrenOf( tree, node, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum ) );
            break;
          }
          wxTreeItemId existing = tree->GetLastChild( tree->GetRootItem() );
          while( existing.IsOk() ) {
            wxTreeItemId next = tree->GetPrevSibling( existing );
            if( tree->GetChildrenCount( existing ) > 0 ) {
              tree->SetItemBold( existing, true );
              firstItem = existing;
              haveFirstItem = true;
            } else {
              tree->Delete( existing );
              existing = next;
              continue;
            }
            existing = tree->GetPrevSibling( existing );
          }
        } else
        { AddChildrenOf( tree, node, data->m_Token->m_Index, ~( tkNamespace | tkClass | tkEnum ) ); }
        break;
      }
      case sfRoot:
      case sfBase:
      case sfDerived:
      default:
        break;
    }
  }
  if( bottom ) {
    tree->ExpandAll();
    if( haveFirstItem && firstItem.IsOk() ) {
      tree->ScrollTo( firstItem );
      tree->EnsureVisible( firstItem );
    }
    tree->Thaw();
  }
}

bool ClassBrowserBuilderThread::AddNodes( CCTreeCtrl* tree, wxTreeItemId parent, const TokenIdxSet* tokens,
    short int tokenKindMask, int tokenScopeMask, bool allowGlobals ) {
  int count = 0;
  std::set<unsigned long, std::less<unsigned long> > tickets;
  if( parent.IsOk() && tree == m_CCTreeCtrlTop ) {
    wxTreeItemIdValue cookie;
    wxTreeItemId curchild = tree->GetFirstChild( parent, cookie );
    while( curchild.IsOk() ) {
      CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( tree->GetItemData( curchild ) );
      curchild = tree->GetNextSibling( curchild );
      if( data && data->m_Ticket ) {
        tickets.insert( data->m_Ticket );
      }
    }
  }
  TokenIdxSet::const_iterator end = tokens->end();
  for( TokenIdxSet::const_iterator start = tokens->begin(); start != end; ++start ) {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    Token* token = m_TokenTree->at( *start );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    if( token
        && ( token->m_TokenKind & tokenKindMask )
        && ( tokenScopeMask == 0 || token->m_Scope == tokenScopeMask )
        && ( allowGlobals || token->m_IsLocal || TokenMatchesFilter( token ) ) ) {
      if( tree == m_CCTreeCtrlTop
          && tickets.find( token->GetTicket() ) != tickets.end() ) {
        continue;
      }
      ++count;
      int img = m_NativeParser->GetTokenKindImage( token );
      wxString str = token->m_Name;
      if( ( token->m_TokenKind == tkFunction ) || ( token->m_TokenKind == tkConstructor ) || ( token->m_TokenKind == tkDestructor )
          || ( token->m_TokenKind == tkMacroUse ) || ( token->m_TokenKind == tkClass ) ) {
        str << token->GetFormattedArgs();
      }
      if( !token->m_FullType.IsEmpty() ) {
        str = str + _T( " : " ) + token->m_FullType + token->m_TemplateArgument;
      }
      wxTreeItemId child = tree->AppendItem( parent, str, img, img, new CCTreeCtrlData( sfToken, token, tokenKindMask ) );
      int kind = tkClass | tkNamespace | tkEnum;
      if( token->m_TokenKind == tkClass ) {
        if( !m_BrowserOptions.treeMembers ) {
          kind |= tkTypedef | tkFunction | tkVariable | tkEnum | tkMacroUse;
        }
        tree->SetItemHasChildren( child, m_BrowserOptions.showInheritance || TokenContainsChildrenOfKind( token, kind ) );
      } else if( token->m_TokenKind & ( tkNamespace | tkEnum ) ) {
        if( !m_BrowserOptions.treeMembers ) {
          kind |= tkTypedef | tkFunction | tkVariable | tkEnumerator | tkMacroUse;
        }
        tree->SetItemHasChildren( child, TokenContainsChildrenOfKind( token, kind ) );
      }
    }
  }
  tree->SortChildren( parent );
  return count != 0;
}

bool ClassBrowserBuilderThread::TokenMatchesFilter( const Token* token, bool locked ) {
  if( !token || token->m_IsTemp ) {
    return false;
  }
  if( m_BrowserOptions.displayFilter == bdfEverything
      || ( m_BrowserOptions.displayFilter == bdfWorkspace && token->m_IsLocal ) ) {
    return true;
  }
  if( m_BrowserOptions.displayFilter == bdfFile && !m_CurrentTokenSet.empty() ) {
    if( m_CurrentTokenSet.find( token->m_Index ) != m_CurrentTokenSet.end() ) {
      return true;
    }
    for( TokenIdxSet::const_iterator tis_it = token->m_Children.begin(); tis_it != token->m_Children.end(); ++tis_it ) {
      if( !locked )
        CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
        const Token* curr_token = m_TokenTree->at( *tis_it );
      if( !locked )
        CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
        if( !curr_token ) {
          break;
        }
      if( TokenMatchesFilter( curr_token, locked ) ) {
        return true;
      }
    }
  } else if( m_BrowserOptions.displayFilter == bdfProject && m_UserData ) {
    return token->m_UserData == m_UserData;
  }
  return false;
}

bool ClassBrowserBuilderThread::TokenContainsChildrenOfKind( const Token* token, int kind ) {
  if( !token ) {
    return false;
  }
  bool isOfKind = false;
  const TokenTree* tree = token->GetTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  for( TokenIdxSet::const_iterator tis_it = token->m_Children.begin(); tis_it != token->m_Children.end(); ++tis_it ) {
    const Token* child = tree->at( *tis_it );
    if( child->m_TokenKind & kind ) {
      isOfKind = true;
      break;
    }
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return isOfKind;
}

void ClassBrowserBuilderThread::SaveExpandedItems( CCTreeCtrl* tree, wxTreeItemId parent, int level ) {
  if( CBBT_SANITY_CHECK ) {
    return;
  }
  wxTreeItemIdValue cookie;
  wxTreeItemId existing = tree->GetFirstChild( parent, cookie );
  while( existing.IsOk() ) {
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( tree->GetItemData( existing ) );
    if( tree->GetChildrenCount( existing, false ) > 0 ) {
      m_ExpandedVect.push_back( CCTreeCtrlExpandedItemData( data, level ) );
      SaveExpandedItems( tree, existing, level + 1 );
    }
    existing = tree->GetNextSibling( existing );
  }
}

void ClassBrowserBuilderThread::ExpandSavedItems( CCTreeCtrl* tree, wxTreeItemId parent, int level ) {
  if( CBBT_SANITY_CHECK ) {
    return;
  }
  wxTreeItemIdValue cookie;
  wxTreeItemId existing = tree->GetFirstChild( parent, cookie );
  while( existing.IsOk() && !m_ExpandedVect.empty() ) {
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( tree->GetItemData( existing ) );
    CCTreeCtrlExpandedItemData saved = m_ExpandedVect.front();
    if( level == saved.GetLevel()
        && wxStrcmp( data->m_TokenName, saved.GetData().m_TokenName ) == 0
        && data->m_TokenKind == saved.GetData().m_TokenKind
        && data->m_SpecialFolder == saved.GetData().m_SpecialFolder ) {
      tree->Expand( existing );
      m_ExpandedVect.pop_front();
      if( m_ExpandedVect.empty() ) {
        return;
      }
      saved = m_ExpandedVect.front();
      if( saved.GetLevel() < level ) {
        return;
      }
      if( saved.GetLevel() > level ) {
        ExpandSavedItems( tree, existing, saved.GetLevel() );
      }
    }
    existing = tree->GetNextSibling( existing );
  }
  while( !m_ExpandedVect.empty() && m_ExpandedVect.front().GetLevel() > level ) {
    m_ExpandedVect.pop_front();
  }
}

void ClassBrowserBuilderThread::SaveSelectedItem() {
  if( CBBT_SANITY_CHECK ) {
    return;
  }
  m_SelectedPath.clear();
  wxTreeItemId item = m_CCTreeCtrlTop->GetSelection();
  while( item.IsOk() && item != m_CCTreeCtrlTop->GetRootItem() ) {
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( m_CCTreeCtrlTop->GetItemData( item ) );
    m_SelectedPath.push_front( *data );
    item = m_CCTreeCtrlTop->GetItemParent( item );
  }
}

void ClassBrowserBuilderThread::SelectSavedItem() {
  if( CBBT_SANITY_CHECK ) {
    return;
  }
  wxTreeItemId parent = m_CCTreeCtrlTop->GetRootItem();
  wxTreeItemIdValue cookie;
  wxTreeItemId item = m_CCTreeCtrlTop->GetFirstChild( parent, cookie );
  while( !m_SelectedPath.empty() && item.IsOk() ) {
    CCTreeCtrlData* data = static_cast<CCTreeCtrlData*>( m_CCTreeCtrlTop->GetItemData( item ) );
    CCTreeCtrlData* saved = &m_SelectedPath.front();
    if( data->m_SpecialFolder == saved->m_SpecialFolder
        && wxStrcmp( data->m_TokenName, saved->m_TokenName ) == 0
        && data->m_TokenKind == saved->m_TokenKind ) {
      wxTreeItemIdValue cookie2;
      parent = item;
      item = m_CCTreeCtrlTop->GetFirstChild( item, cookie2 );
      m_SelectedPath.pop_front();
    } else
    { item = m_CCTreeCtrlTop->GetNextSibling( item ); }
  }
  if( parent.IsOk() ) {
    m_SelectItemRequired = parent;
    wxCommandEvent e( wxEVT_COMMAND_ENTER, m_idThreadEvent );
    e.SetInt( selectItemRequired );
    m_Parent->AddPendingEvent( e );
  }
  m_SelectedPath.clear();
}
